package com.qiwenshare.file.util;

import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.pdf.*;
import com.microsoft.schemas.vml.CTShape;
import com.qiwenshare.ufop.util.UFOPUtils;
import java.awt.font.FontRenderContext;
import javax.xml.namespace.QName;
import org.apache.poi.hslf.usermodel.HSLFPictureShape;
import org.apache.poi.hslf.usermodel.HSLFSlide;
import org.apache.poi.hslf.usermodel.HSLFSlideShow;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.sl.usermodel.PictureData;
import org.apache.poi.sl.usermodel.PictureData.PictureType;
import org.apache.poi.sl.usermodel.SlideShow;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xslf.usermodel.XSLFPictureShape;
import org.apache.poi.xslf.usermodel.XSLFSlide;
import org.apache.poi.xssf.usermodel.XSSFRelation;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import javax.imageio.ImageIO;
import java.awt.Color;
import java.awt.Font;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.*;
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFHeader;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPicture;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR.Factory;

public class WaterMarkUtils {
    private static String path = "C:\\Users\\TRX\\Desktop\\watermark\\";

    public static void setWordWaterMarkInputStream(InputStream inputStream, OutputStream outputStream, String markStr, String fileType) throws Exception {
        if (fileType.startsWith("docx")) {
            addWaterMarkForWord(inputStream, outputStream, markStr, fileType);
        } else if (fileType.startsWith("xls")){
            // addWaterMarkForExcelXSSFByBack(inputStream, outputStream, markStr, fileType);
            addWaterMarkForExcelByPic(inputStream, outputStream, markStr, fileType);
        } else if (fileType.startsWith("pdf")) {
            addWaterMarkForPdf(inputStream, outputStream, markStr);
        } else if (UFOPUtils.isImageFile(fileType)) {
            addWaterMarkForImage(inputStream, outputStream, markStr, fileType);
        } else if (fileType.startsWith("ppt")) {
            addWaterMarkForPpt(inputStream, outputStream, markStr, fileType);
        }
    }

    private static void addWaterMarkForPpt(InputStream inputStream, OutputStream outputStream, String markStr, String fileType) throws IOException {
        //设置水印图片路径
        BufferedImage watermarkImage = createWaterMarkImage(markStr);
        ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
        ImageIO.write(watermarkImage, "png", byteArrayOut);
        byte[] imgBytes = byteArrayOut.toByteArray();
        if("pptx".equals(fileType)) {
            XMLSlideShow slideShow = new XMLSlideShow(inputStream);
            PictureData pictureData1 = slideShow.addPicture(imgBytes, PictureType.PNG);
            for (XSLFSlide slide : slideShow.getSlides()) {
                XSLFPictureShape pictureShape = slide.createPicture(pictureData1);
                pictureShape.setAnchor(new java.awt.Rectangle(50, 300, 100, 100));
            }
            slideShow.write(outputStream);
            slideShow.close();
        } else if("ppt".equals(fileType)) {
            SlideShow slideShow = new HSLFSlideShow(inputStream);
            PictureData pictureData1 = slideShow.addPicture(imgBytes, PictureType.PNG);
            for (Object object : slideShow.getSlides()) {
                HSLFSlide slide = (HSLFSlide) object;
                HSLFPictureShape pictureShape = slide.createPicture(pictureData1);
                pictureShape.setAnchor(new java.awt.Rectangle(50, 300, 100, 100));
            }
            slideShow.write(outputStream);
            slideShow.close();
        }
    }

    /**
     * 根据文字生成水印图片
     * @param content
     */
    public static BufferedImage createWaterMarkImage(String content) {
        Integer width = 300;
        Integer height = 200;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);// 获取bufferedImage对象
        String fontType = "宋体";
        Integer fontStyle = Font.PLAIN;
        Integer fontSize = 50;
        Font font = new Font(fontType, fontStyle, fontSize);
        Graphics2D g2d = image.createGraphics(); // 获取Graphics2d对象
        image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
        g2d.dispose();
        g2d = image.createGraphics();
        //g2d.setColor(new Color(0, 0, 0, 80)); //设置字体颜色和透明度
        g2d.setColor(new Color(255, 180, 0, 80)); //设置字体颜色和透明度
        g2d.setStroke(new BasicStroke(1)); // 设置字体
        g2d.setFont(font); // 设置字体类型  加粗 大小
        g2d.rotate(Math.toRadians(-10), (double) image.getWidth() / 2, (double) image.getHeight() / 2);//设置倾斜度
        FontRenderContext context = g2d.getFontRenderContext();
        Rectangle2D bounds = font.getStringBounds(content, context);
        double x = (width - bounds.getWidth()) / 2;
        double y = (height - bounds.getHeight()) / 2;
        double ascent = -bounds.getY();
        double baseY = y + ascent;
        // 写入水印文字原定高度过小，所以累计写水印，增加高度
        g2d.drawString(content, (int) x, (int) baseY);
        // 设置透明度
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
        // 释放对象
        g2d.dispose();
        return image;
    }

    private static void addWaterMarkForImage(InputStream inputStream, OutputStream outputStream, String markStr, String fileType) throws IOException {
        BufferedImage image = ImageIO.read(inputStream); // 读取图片
        int imgWidth = image.getWidth(null);
        int imgHeight = image.getHeight(null);
        BufferedImage bufImg = new BufferedImage(imgWidth, imgHeight, image.getType());
        // 创建画笔
        Graphics2D graphics = bufImg.createGraphics();
        // 绘制原始图片
        graphics.drawImage(image, 0, 0, imgWidth, imgHeight, null);
        // 设置水印颜色
        graphics.setColor(Color.red);
        // 设置水印透明度
        graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f));
        // 设置倾斜角度
        graphics.rotate(Math.toRadians(-25), (double) bufImg.getWidth() / 2, (double) bufImg.getHeight() / 2);
        // 设置字体  画笔字体样式为微软雅黑，文字大小为(Math.min(imgWidth, imgHeight) / 5)pt
        graphics.setFont(new Font("微软雅黑", Font.PLAIN, Math.min(imgWidth, imgHeight) / 20));
        // 消除java.awt.Font字体的锯齿
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // // 设置水印的坐标(为原图片中间位置)
        // int x = (imgWidth - graphics.getFontMetrics(graphics.getFont()).charsWidth(markStr.toCharArray(), 0, markStr.length())) / 2;
        // int y = imgHeight / 2;
        // // 画出水印 第一个参数是水印内容，第二个参数是x轴坐标，第三个参数是y轴坐标
        // graphics.drawString(markStr, x, y);
        //每页3行，一行2个
        int rowNum = 3; // 行数
        int columnNum = 2; // 列数
        for (int j=1; j<=columnNum; j++) {
            for (int k=1; k<=rowNum; k++) {
                //showTextAligned 方法的参数（文字对齐方式，位置内容，输出水印X轴位置，Y轴位置，旋转角度）
                graphics.drawString(markStr, imgWidth/(columnNum+1)*j, imgHeight/(rowNum+1)*k);
            }
        }
        graphics.dispose();
        // 输出图片
        ImageIO.write(bufImg, fileType, outputStream);
    }

    private static void addWaterMarkForPdf(InputStream inputStream, OutputStream outputStream, String markStr) throws IOException, DocumentException {
        BufferedOutputStream bufferOut = new BufferedOutputStream(outputStream);
        PdfReader reader = new PdfReader(inputStream);
        PdfStamper stamper = new PdfStamper(reader, bufferOut);

        //使用系统字体
        // String prefixFont = null;
        // String os = System.getProperties().getProperty("os.name");
        // if (os.startsWith("win") || os.startsWith("Win")) {
        //     prefixFont = "C:\\Windows\\Fonts\\SIMSUN.TTC,1";
        // } else {
        //     prefixFont = "/usr/share/fonts/chinese/TrueType/uming.ttf";
        // }
        //创建字体，第一个参数是字体路径
        // BaseFont base = BaseFont.createFont(prefixFont, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);

        PdfGState gs = new PdfGState();
        gs.setFillOpacity(0.2f);//图片水印透明度
        //gs.setStrokeOpacity(0.4f);//设置笔触字体不透明度
        PdfContentByte content = null;
        int total = reader.getNumberOfPages();//pdf文件页数
        for (int i=0; i<total; i++) {
            float x = reader.getPageSize(i+1).getWidth();//页宽度
            float y = reader.getPageSize(i+1).getHeight();//页高度
            content = stamper.getOverContent(i+1);
            content.setGState(gs);
            content.beginText();//开始写入
            content.setFontAndSize(base, Math.min(x, y) / 20);//字体大小
            //每页3行，一行2个
            int rowNum = 3; // 行数
            int columnNum = 2; // 列数
            for (int j=1; j<=columnNum; j++) {
                for (int k=1; k<=rowNum; k++) {
                    //showTextAligned 方法的参数（文字对齐方式，位置内容，输出水印X轴位置，Y轴位置，旋转角度）
                    content.showTextAligned(Element.ALIGN_CENTER, markStr, x/(columnNum+1)*j, y/(rowNum+1)*k, 25);
                }
            }
            content.endText();//结束写入
        }
        //关闭流
        stamper.close();
        reader.close();
    }

    private static void addWaterMarkForExcelXSSFByBack(InputStream inputStream, OutputStream outputStream, String markStr, String fileType) throws IOException {
        XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
        // 创建水印的图
        BufferedImage watermarkImage = createWaterMarkImage(markStr);
        ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
        ImageIO.write(watermarkImage, "png", byteArrayOut);
        int pictureIdx = workbook.addPicture(byteArrayOut.toByteArray(), Workbook.PICTURE_TYPE_PNG);
        // 遍历表格的sheet页
        for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
            XSSFSheet xssfSheet =  workbook.getSheetAt(i);
            String rID = xssfSheet.addRelation(null, XSSFRelation.IMAGE_PNG, workbook.getAllPictures().get(pictureIdx))
                    .getRelationship().getId();
            //set background picture to sheet
            xssfSheet.getCTWorksheet().addNewPicture().setId(rID);
        }
        workbook.write(outputStream);
    }

    public static void addWaterMarkForExcelByPic(InputStream inputStream, OutputStream outputStream, String markStr, String fileType){
        try {
            //读取excel文件
            Workbook wb =null;
            if (fileType.endsWith("xls")) {
                wb = new HSSFWorkbook(inputStream);
            }else if (fileType.endsWith("xlsx")){
                wb = new XSSFWorkbook(inputStream);
            }
            //获取excel sheet个数
            int sheets = wb.getNumberOfSheets();
            //循环sheet给每个sheet添加水印
            for (int i = 0; i < sheets; i++) {
                Sheet sheet = wb.getSheetAt(i);
                //获取excel实际所占行
                int row = sheet.getFirstRowNum() + sheet.getLastRowNum();
                //获取excel实际所占列
                Row row1 = sheet.getRow(sheet.getFirstRowNum());
                if (row1==null){
                    continue;
                }
                int cell = sheet.getRow(sheet.getFirstRowNum()).getLastCellNum() + 1;
                //根据行与列计算实际所需多少水印
                putWaterRemarkToExcel(wb, sheet, markStr, 0, 0, 6, 8, cell / 6 + 1, row / 8 + 1, 0, 0);

                //设置为受保护
                // sheet.protectSheet(waterMarkName);
            }
            wb.write(outputStream);
            wb.close();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    /**
     *
     * @param wb   Excel Workbook
     * @param sheet  需要打水印的Excel
     * @param waterRemark  水印文字
     * @param startXCol   水印起始列
     * @param startYRow  水印起始行
     * @param betweenXCol  水印横向之间间隔多少列
     * @param betweenYRow  水印纵向之间间隔多少行
     * @param XCount    横向共有水印多少个
     * @param YCount    纵向共有水印多少个
     * @param waterRemarkWidth   水印图片宽度为多少列
     * @param waterRemarkHeight   水印图片高度为多少行
     * @throws IOException
     */
    public static void putWaterRemarkToExcel(Workbook wb, Sheet sheet, String waterRemark, int startXCol,
            int startYRow, int betweenXCol, int betweenYRow, int XCount, int YCount, int waterRemarkWidth,
            int waterRemarkHeight) throws IOException {

        // 加载图片
        ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
        BufferedImage bufferImg = createWaterMarkImage(waterRemark);
        ImageIO.write(bufferImg, "png", byteArrayOut);
        // 开始打水印
        Drawing drawing = sheet.createDrawingPatriarch();
        Drawing<?> drawingPatriarch = sheet.getDrawingPatriarch();
        // 按照共需打印多少行水印进行循环
        for (int yCount = 0; yCount < YCount; yCount++) {
            // 按照每行需要打印多少个水印进行循环
            for (int xCount = 0; xCount < XCount; xCount++) {
                // 创建水印图片位置
                int xIndexInteger = startXCol + (xCount * waterRemarkWidth) + (xCount * betweenXCol);
                int yIndexInteger = startYRow + (yCount * waterRemarkHeight) + (yCount * betweenYRow);
                /*
                 * 参数定义： 第一个参数是（x轴的开始节点）； 第二个参数是（是y轴的开始节点）； 第三个参数是（是x轴的结束节点）；
                 * 第四个参数是（是y轴的结束节点）； 第五个参数是（是从Excel的第几列开始插入图片，从0开始计数）；
                 * 第六个参数是（是从excel的第几行开始插入图片，从0开始计数）； 第七个参数是（图片宽度，共多少列）；
                 * 第8个参数是（图片高度，共多少行）；
                 */
                ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, xIndexInteger,
                        yIndexInteger, xIndexInteger + waterRemarkWidth, yIndexInteger + waterRemarkHeight);

                Picture pic = drawing.createPicture(anchor,
                        wb.addPicture(byteArrayOut.toByteArray(), Workbook.PICTURE_TYPE_PNG));
                pic.resize();
            }
        }
    }

    private static void addWaterMarkForWord(InputStream inputStream, OutputStream outputStream, String markStr, String fileType) {
        XWPFDocument doc = null;
        try {
            doc = new XWPFDocument(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // create header-footer
        XWPFHeaderFooterPolicy headerFooterPolicy = doc.getHeaderFooterPolicy();
        if (headerFooterPolicy == null) headerFooterPolicy = doc.createHeaderFooterPolicy();
        // create default Watermark - fill color black and not rotated
        headerFooterPolicy.createWatermark(markStr);
        // get the default header
        // Note: createWatermark also sets FIRST and EVEN headers
        // but this code does not updating those other headers
        XWPFHeader header = headerFooterPolicy.getHeader(XWPFHeaderFooterPolicy.DEFAULT);
        int size = header.getParagraphs().size();
        if (size == 0) {
            header.createParagraph();
        }
        XWPFParagraph paragraph = header.getParagraphArray(0);
        // System.out.println(paragraph.getCTP().getRArray(0));
        // System.out.println(paragraph.getCTP().getRArray(0).getPictArray(0));
        // get com.microsoft.schemas.vml.CTShape where fill color and rotation is set
        if (paragraph.getCTP().getRArray().length == 0) {
            paragraph.getCTP().setRArray(new CTR[]{Factory.newInstance()});
            paragraph.getCTP().getRArray(0).setPictArray(new CTPicture[]{CTPicture.Factory.newInstance()});
            CTShape ctshape = CTShape.Factory.newInstance();
            paragraph.getCTP().getRArray(0).getPictArray(0).set(ctshape);
        }
            XmlObject[] xmlobjects = paragraph.getCTP().getRArray(0).getPictArray(0).selectChildren(
                    new QName("urn:schemas-microsoft-com:vml", "shape"));
            if (xmlobjects.length > 0) {
                CTShape ctshape = (CTShape)xmlobjects[0];
                // set fill color
                //ctshape.setFillcolor("#d8d8d8");
                ctshape.setFillcolor("#CC00FF");
                // set rotation
                ctshape.setStyle(ctshape.getStyle() + ";rotation:315");
                //System.out.println(ctshape);
            }


        try {
            doc.write(outputStream);
            doc.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public static void main(String[] args) throws Exception {
        // String extend = "pptx";
        // String extend = "pdf";
        String extend = "xlsx";
        String waterWord = "我是水印";
        FileInputStream fis = new FileInputStream(path + "abc." + extend);
        FileOutputStream fos = new FileOutputStream(path + "abctest23." + extend);
        setWordWaterMarkInputStream(fis, fos, waterWord, extend);
    }


}